import os
import shutil
import uuid
from pathlib import Path
from typing import Optional, Tuple
import ffmpeg
from loguru import logger
from config import config

class FileUtils:
    """文件处理工具类"""
    
    @staticmethod
    def validate_video_file(file_path: str) -> Tuple[bool, str]:
        """
        验证视频文件
        
        Args:
            file_path: 文件路径
            
        Returns:
            (是否有效, 错误信息)
        """
        try:
            # 检查文件是否存在
            if not os.path.exists(file_path):
                return False, "文件不存在"
            
            # 检查文件大小
            file_size = os.path.getsize(file_path)
            if file_size > config.MAX_FILE_SIZE:
                return False, f"文件大小超过限制 ({file_size} > {config.MAX_FILE_SIZE})"
            
            # 检查文件扩展名
            file_ext = Path(file_path).suffix.lower()
            if file_ext not in config.SUPPORTED_VIDEO_FORMATS:
                return False, f"不支持的文件格式: {file_ext}"
            
            # 使用ffmpeg检查文件是否为有效视频
            try:
                probe = ffmpeg.probe(file_path)
                if not probe or 'streams' not in probe:
                    return False, "无法读取视频流信息"
                
                # 检查是否有视频流
                video_streams = [s for s in probe['streams'] if s['codec_type'] == 'video']
                if not video_streams:
                    return False, "文件中没有视频流"
                
                return True, "文件验证通过"
                
            except ffmpeg.Error as e:
                return False, f"视频文件格式错误: {str(e)}"
                
        except Exception as e:
            return False, f"文件验证失败: {str(e)}"
    
    @staticmethod
    def get_video_info(file_path: str) -> dict:
        """
        获取视频信息
        
        Args:
            file_path: 视频文件路径
            
        Returns:
            视频信息字典
        """
        try:
            probe = ffmpeg.probe(file_path)
            
            # 获取视频流信息
            video_stream = next((s for s in probe['streams'] if s['codec_type'] == 'video'), None)
            
            # 获取音频流信息
            audio_stream = next((s for s in probe['streams'] if s['codec_type'] == 'audio'), None)
            
            # 获取格式信息
            format_info = probe.get('format', {})
            
            info = {
                'duration': float(format_info.get('duration', 0)),
                'file_size': int(format_info.get('size', 0)),
                'bit_rate': int(format_info.get('bit_rate', 0)),
                'format': format_info.get('format_name', ''),
            }
            
            if video_stream:
                info.update({
                    'resolution': f"{video_stream.get('width', 0)}x{video_stream.get('height', 0)}",
                    'frame_rate': eval(video_stream.get('r_frame_rate', '0/1')),
                    'video_codec': video_stream.get('codec_name', ''),
                })
            
            if audio_stream:
                info.update({
                    'audio_codec': audio_stream.get('codec_name', ''),
                    'sample_rate': int(audio_stream.get('sample_rate', 0)),
                    'channels': int(audio_stream.get('channels', 0)),
                })
            
            return info
            
        except Exception as e:
            logger.error(f"获取视频信息失败: {e}")
            return {}
    
    @staticmethod
    def save_uploaded_file(uploaded_file, original_filename: str) -> Tuple[str, str]:
        """
        保存上传的文件
        
        Args:
            uploaded_file: 上传的文件对象
            original_filename: 原始文件名
            
        Returns:
            (存储的文件路径, 存储的文件名)
        """
        try:
            # 生成唯一的存储文件名
            file_ext = Path(original_filename).suffix
            stored_filename = f"{uuid.uuid4()}{file_ext}"
            file_path = config.UPLOAD_DIR / stored_filename
            
            # 保存文件
            with open(file_path, "wb") as buffer:
                shutil.copyfileobj(uploaded_file.file, buffer)
            
            logger.info(f"文件保存成功: {file_path}")
            return str(file_path), stored_filename
            
        except Exception as e:
            logger.error(f"保存文件失败: {e}")
            raise
    
    @staticmethod
    def extract_audio(video_path: str, output_path: Optional[str] = None) -> str:
        """
        从视频中提取音频
        
        Args:
            video_path: 视频文件路径
            output_path: 输出音频路径（可选）
            
        Returns:
            音频文件路径
        """
        try:
            if output_path is None:
                # 生成默认输出路径
                video_name = Path(video_path).stem
                output_path = config.AUDIO_DIR / f"{video_name}_audio.wav"
            
            # 确保输出目录存在
            Path(output_path).parent.mkdir(parents=True, exist_ok=True)
            
            # 使用ffmpeg提取音频
            stream = ffmpeg.input(video_path)
            stream = ffmpeg.output(stream, str(output_path), acodec='pcm_s16le', ac=1, ar='16000')
            ffmpeg.run(stream, overwrite_output=True, quiet=True)
            
            logger.info(f"音频提取成功: {output_path}")
            return str(output_path)
            
        except Exception as e:
            logger.error(f"音频提取失败: {e}")
            raise
    
    @staticmethod
    def delete_file(file_path: str) -> bool:
        """
        删除文件
        
        Args:
            file_path: 文件路径
            
        Returns:
            是否删除成功
        """
        try:
            if os.path.exists(file_path):
                os.remove(file_path)
                logger.info(f"文件删除成功: {file_path}")
                return True
            return False
        except Exception as e:
            logger.error(f"删除文件失败: {e}")
            return False
    
    @staticmethod
    def get_file_size(file_path: str) -> int:
        """
        获取文件大小
        
        Args:
            file_path: 文件路径
            
        Returns:
            文件大小（字节）
        """
        try:
            return os.path.getsize(file_path)
        except Exception as e:
            logger.error(f"获取文件大小失败: {e}")
            return 0 